package pa4.impl;

import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.Timer;
import java.util.TimerTask;

import pa4.interfaces.Node;
import pa4.interfaces.Peer;
import pa4.interfaces.SystemManager;
import pa4.util.IntegerComp;

public class SystemManagerImpl<V> extends UnicastRemoteObject implements
		SystemManager<V>, Runnable {

	private ArrayList<Peer<V>> peers = new ArrayList<Peer<V>>();
	private int numPeers = 0;
	private ArrayList<Node<V>> rootNodeReplicas = new ArrayList<Node<V>>();
	private ArrayList<Peer<V>> registeredPeers = new ArrayList<Peer<V>>();
	private boolean init = true;
	private Node<V> rootNode;
	private Timer restructureTimer = new Timer();
	public SystemManagerImpl() throws RemoteException {
		super();
		// TODO Auto-generated constructor stub
	}

	@Override
	public synchronized ArrayList<Peer<V>> getRegisteredPeers(Peer<V> peer) throws RemoteException {
		return registeredPeers;
	}
	

	private void buildTree() {
		IntegerComp id = new IntegerComp(Integer.MAX_VALUE/2);
		while(rootNodeReplicas.size() < SystemManager.NR_REPLICAS && peers.size() >= 3){
			//replicateNode(id, false, null);
		}
		
	}

	@Override
	public synchronized Node<V> replicateNode(int id, boolean isLeaf, Peer<V> peer) throws RemoteException {
		/*
		//get the peer with the fewest received messages to host the new replication
		int min = Integer.MAX_VALUE;
		Peer<K,V> newPeer = null;
		for(Peer<K,V> p : peers){
			if(!p.equals(peer)){
				if(p.getNumberOfNodes() <= min){
					newPeer = p;
				}
			}
		}
		if(newPeer != null)
			return newPeer.createNode(id, isLeaf);
		else return null;
		*/
		return null;
	}

	@Override
	public void run() {
		String port = Integer.toString(Registry.REGISTRY_PORT);

		try {
			Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
			registry.rebind(SystemManager.SERVICENAME, this);
			System.out.println("Bound SystemManager in local registry.");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public synchronized void join(Peer<V> peer) throws RemoteException {
		//numPeers++;
		registeredPeers.add(peer);
		peer.pushRegisteredPeerList(registeredPeers);
		//build the tree
		if(registeredPeers.size() >= 4 && init){
			rootNode = registeredPeers.get(0).createNode(Integer.MAX_VALUE/2, false, 1, true);
			restructureTimer.schedule(new RestructureTreeTask(this), 200);
		}
	}

	@Override
	public Node<V> getRoot() throws RemoteException {
		return rootNode;
	}

	public void printStatistics() {
		for(Peer peer : registeredPeers){
			try {
				System.out.println("Peer " + peer.getName() + ":");
				System.out.println("Number of hosted nodes: " + peer.getNumberOfNodes());
				System.out.println("Number of stored values: " + peer.getNumberOfStoredValues());
				System.out.println("Number of received Messages: " + peer.getNumberOfReceivedMessages());
				System.out.println("------------------------------------------------------------------");
			} catch (RemoteException e) {
				e.printStackTrace();
			}
		}
	}
	
	class RestructureTreeTask extends TimerTask{
		private SystemManagerImpl<V> context;
		
		public RestructureTreeTask(SystemManagerImpl<V> context){
			this.context = context;
		}

		@Override
		public void run() {
			context.restructureTree();
		}
	}

	public synchronized void restructureTree() {
		try {
			//get the peer with the fewest received messages and migrate the root node to it
			Peer<V> newPeer = rootNode.getPeer();
			int min = rootNode.getPeer().getNumberOfReceivedMessages();
			for(Peer<V> peer : registeredPeers){
				if(peer.getNumberOfReceivedMessages() < min){
					newPeer = peer;
				}
			}
			if(!newPeer.getName().equalsIgnoreCase(rootNode.getPeer().getName())){
				//get the children from the old root
				Node<V> leftChild = rootNode.getLeftChild();
				Node<V> rightChild = rootNode.getRightChild();
				
				rootNode.getPeer().removeNode(rootNode.getId());
				//create new RootNode
				rootNode = newPeer.createNode(Integer.MAX_VALUE/2, false, 1, false);
				rootNode.setLeftChild(leftChild);
				rootNode.setRightChild(rightChild);				
			}
			restructureTimer.schedule(new RestructureTreeTask(this), 5000);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	public synchronized void put(int key, V value) throws RemoteException{
		rootNode.put(key, value);
	}
	
	public synchronized V get(int key) throws RemoteException{
		return rootNode.get(key);
	}
	
	public synchronized void remove(int key) throws RemoteException{
		rootNode.remove(key);
	}

}
